#include "wen_importerWithAssimp.h"
#include "sceneGraph/wen_node.h"
#include "sceneGraph/wen_geometry.h"

using namespace wen;

ImporterWithAssimp::ImporterWithAssimp()
:
m_pkRootNode(NULL),
m_pAssimpLoadedScene(NULL),
m_strGlobalPath("")
{
}

ImporterWithAssimp::~ImporterWithAssimp()
{
}

bool ImporterWithAssimp::Load(const std::string& rkFilename)
{
	unsigned int bar = (unsigned int)rkFilename.find_last_of('/');
	m_strGlobalPath = rkFilename.substr(0, bar + 1);

	// Create an instance of the Importer class
	Assimp::Importer importer;

	// And have it read the given file with some example postprocessing
	// Usually - if speed is not the most important aspect for you - you'll
	// propably to request more postprocessing than we do in this example.
	m_pAssimpLoadedScene = importer.ReadFile(rkFilename,
    aiPrimitiveType_LINE |
    aiPrimitiveType_POINT |
    aiProcess_Triangulate |
	/*aiProcess_MakeLeftHanded |*/ /*This Flags is needed beacuse DirectX use Left Handed coordinate system*/ //Anda Mal
    aiProcess_JoinIdenticalVertices |
    aiProcess_CalcTangentSpace |
    aiProcess_SortByPType |
	aiProcess_FlipUVs |
	aiProcess_RemoveRedundantMaterials |
	aiProcess_TransformUVCoords);

	aiNode* assimpRootNode = m_pAssimpLoadedScene->mRootNode; //The Root node of the scene

	ImportNode(assimpRootNode, m_pkRootNode); //start to check all the nodes

	return true;
}

void ImporterWithAssimp::ImportNode(aiNode* assimpNode, Node* parentNode)
{
	Node* childNode = new Node();

	//Get and save the name
	aiString nodeName = assimpNode->mName;
    string name(nodeName.C_Str());
    childNode->SetName(name);   

	//Set the Type
	childNode->type = TYPE_NODE;

	if(parentNode == NULL)
	{		
		m_pkRootNode = childNode;		
	}
	else
	{		
		parentNode->AddChild(childNode);
	}	

	//Import Node transformation Matrix
	ImportTransformationMatrix(assimpNode, childNode);

	//Import Meshes
	for(unsigned int i = 0; i < assimpNode->mNumMeshes; i++)
	{
		aiMesh* tmpAssimpMesh = m_pAssimpLoadedScene->mMeshes[assimpNode->mMeshes[i]];

		Geometry* theGeometry = ImportGeometry(assimpNode, tmpAssimpMesh);

		theGeometry->type = TYPE_MESH;

		childNode->AddChild(theGeometry);
	}

	//Import the node childs
	for(unsigned int i = 0; i < assimpNode->mNumChildren; i++)
    {
	   aiNode* tmpAssimpNode = assimpNode->mChildren[i];

	   ImportNode(tmpAssimpNode, childNode);
	}
}

void ImporterWithAssimp::ImportTransformationMatrix(aiNode* assimpNode, Node* node)
{
	//aiMatrix4x4 assimpMatrix = assimpNode->mTransformation.Transpose();
	aiMatrix4x4 assimpMatrix = assimpNode->mTransformation;

	//float* values = new float[16];
    //memcpy(values, &assimpMatrix, sizeof(float) * 16);
   
	//node->GetLocalTransformation()->SetTotalTransformMatrixFromArray(values);
	
	aiVector3t<float> pos;
	aiQuaternion rot;
	aiVector3t<float> scale;
	assimpMatrix.Decompose(scale, rot, pos);
	
	node->SetPos(pos.x, pos.y, pos.z);
	node->SetDim(scale.x, scale.y, scale.z);
	node->SetRotationByQuaternion(rot.x, rot.y, rot.z, rot.w);

	node->CalculateMatrix();
}

Geometry* ImporterWithAssimp::ImportGeometry(aiNode* assimpNode, aiMesh* assimpMesh)
{
	Geometry* tmpGeometry = new Geometry();

	//Get and Set the name
	aiString meshName = assimpMesh->mName;
	string name(meshName.C_Str());
	tmpGeometry->SetName(name);

	ImportVertex(assimpMesh, tmpGeometry); //Import the mesh vertices
	ImportIndex(assimpMesh, tmpGeometry); //Import the indexes

	ImportNormal(assimpMesh, tmpGeometry); //Import the normals

	ImportTextureCoords(assimpMesh, tmpGeometry); //Import Texture coords

	ImportMaterial(assimpMesh, tmpGeometry); //Import Materials

	return tmpGeometry;
}

void ImporterWithAssimp::ImportVertex(aiMesh* assimpMesh, Geometry* theGeometry)
{
	unsigned int vertexCount = assimpMesh->mNumVertices; //Get the total Number of vertices
	theGeometry->SetVertexCount(vertexCount);

	theGeometry->SetVB(VertexBuffer<TextureVertex, TEXTURE_VERTEX>::Ptr(new VertexBuffer<TextureVertex, TEXTURE_VERTEX>(vertexCount))); //Create and set vertex buffer

	TextureVertex* vertexPool = new TextureVertex[vertexCount]; //Create vertex pool

	bool firstPass = true;
	//Bhv
	float maxX = 0;
	float minX = 9999;
	float maxY = 0;
	float minY = 9999;
	float maxZ = 0;
	float minZ = 9999;

	for(unsigned int i = 0; i < vertexCount; i++) //Assign all the vertex positions
	{
		//vertexPool[i].fPosX = assimpMesh->mVertices[i].x;		
		//vertexPool[i].fPosY = assimpMesh->mVertices[i].y;		
		//vertexPool[i].fPosZ = assimpMesh->mVertices[i].z;

		float vertexX = assimpMesh->mVertices[i].x;
		float vertexY = assimpMesh->mVertices[i].y;
		float vertexZ = assimpMesh->mVertices[i].z;

		if(firstPass)
		{
			vertexPool[i].fPosX = vertexX;
			vertexPool[i].fPosY = vertexY;
			vertexPool[i].fPosZ = vertexZ;

			maxX = vertexX;
			minX = vertexX;
			maxY = vertexY;
			minY = vertexY;
			maxZ = vertexZ;
			minZ = vertexZ;

			firstPass = false;
		}
		else
		{
			vertexPool[i].fPosX = vertexX;
			if (vertexX > maxX)
			{
				maxX = vertexX;
			}
			if (vertexX < minX)
			{
				minX = vertexX;
			}

			vertexPool[i].fPosY = vertexY;
			if (vertexY > maxY)
			{
				maxY = vertexY;
			}
			if (vertexY < minY)
			{
				minY = vertexY;
			}

			vertexPool[i].fPosZ = vertexZ;
			if (vertexZ > maxZ)
			{
				maxZ = vertexZ;
			}
			if (vertexZ < minZ)
			{
				minZ = vertexZ;
			}
		}
	}

	theGeometry->GetBVH()->SetBVHBoxVertices(maxX, minX, maxY, minY, maxZ, minZ); //Save the Vertices for bvh

	theGeometry->SetVertexPool(vertexPool);
	theGeometry->GetVB()->SetVertices(vertexPool);
}

void ImporterWithAssimp::ImportIndex(aiMesh* assimpMesh, Geometry* theGeometry)
{
	std::vector<unsigned int> indices;

	for(unsigned int i = 0; i < assimpMesh->mNumFaces; i++)
	{
		for(unsigned int j = 0; j < assimpMesh->mFaces[i].mNumIndices; j++)
		{
			indices.push_back(assimpMesh->mFaces[i].mIndices[j]);
		}
	}

	unsigned int primitiveCount = assimpMesh->mNumFaces;
	theGeometry->SetPrimitiveCount(primitiveCount);

	unsigned int indexCount = (unsigned int)indices.size();	
	theGeometry->SetIndexCount(indexCount);

	theGeometry->SetIB(IndexBuffer::Ptr(new IndexBuffer(indexCount))); //Create and set Index Buffer

	short* prsIndexPool = new short[indexCount];				

	for(unsigned int i = 0; i < indexCount; i++) //Assing all the indices
	{
		prsIndexPool[i] = indices[i];
	}

	theGeometry->SetIndexPool(prsIndexPool); //Set the Index pool
}

void ImporterWithAssimp::ImportNormal(aiMesh* assimpMesh, Geometry* theGeometry)
{
	if(assimpMesh->HasNormals())
	{
		int normalCount = (int)assimpMesh->mNormals->Length();

		for(int i = 0; i < normalCount; i++)
		{
			theGeometry->GetVertexPool()[i].fNX = assimpMesh->mNormals[i].x;
			theGeometry->GetVertexPool()[i].fNY = assimpMesh->mNormals[i].y;
			theGeometry->GetVertexPool()[i].fNZ = assimpMesh->mNormals[i].z;
		}
		
		/*for (unsigned int i = 0; i < theGeometry->GetVertexCount(); i++)
		{
			theGeometry->NormalizeNormal(theGeometry->GetVertexPool()[i]);
		}*/
	}
}

void ImporterWithAssimp::ImportTextureCoords(aiMesh* assimpMesh, Geometry* theGeometry)
{
	if(assimpMesh->HasTextureCoords(0))
	{
		int numVertices = assimpMesh->mNumVertices;
		for(int i = 0; i < numVertices; i++)
		{
			theGeometry->GetVertexPool()[i].fU = assimpMesh->mTextureCoords[0][i].x;
			theGeometry->GetVertexPool()[i].fV = assimpMesh->mTextureCoords[0][i].y;
		}
	}
}

void ImporterWithAssimp::ImportMaterial(aiMesh* assimpMesh, Geometry* theGeometry)
{
	aiMaterial* theAiMaterial = m_pAssimpLoadedScene->mMaterials[assimpMesh->mMaterialIndex];

	Material* tmpMaterial = new Material();

	//Get the name
	aiString materialName;
	theAiMaterial->Get(AI_MATKEY_NAME, materialName);

	//Get all the colors
	aiColor4D ambientColor(0.0f, 0.0f, 0.0f, 0.0f);
	theAiMaterial->Get(AI_MATKEY_COLOR_AMBIENT, ambientColor);
	aiColor4D specularColor(0.0f, 0.0f, 0.0f, 0.0f);
	theAiMaterial->Get(AI_MATKEY_COLOR_SPECULAR, specularColor);
	aiColor4D emissiveColor(0.0f, 0.0f, 0.0f, 0.0f);
	theAiMaterial->Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColor);
	aiColor4D diffuseColor(0.0f, 0.0f, 0.0f, 0.0f);
	theAiMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor);

	//Set all the colors to the material
	tmpMaterial->SetAmbientColor(ambientColor.r, ambientColor.g, ambientColor.b, ambientColor.a);
    tmpMaterial->SetDiffuseColor(diffuseColor.r, diffuseColor.g, diffuseColor.b, diffuseColor.a);
    tmpMaterial->SetEmissiveColor(emissiveColor.r, emissiveColor.g, emissiveColor.b, emissiveColor.a);  
    tmpMaterial->SetSpecularColor(specularColor.r, specularColor.g, specularColor.b, specularColor.a);	

	//Get the Power
	float power;
    theAiMaterial->Get(AI_MATKEY_SHININESS, power);
    tmpMaterial->SetPower(power);
 
	//Add the material to the vector
    theGeometry->AddMaterial(tmpMaterial);

	//Get the Texture
	int textureIndex = 0;
    aiString path;
 
    aiReturn textureFound = theAiMaterial->GetTexture(aiTextureType_DIFFUSE, textureIndex, &path);
    string texturePath(path.C_Str());

    if(texturePath.compare("") != 0)
	{
		tmpMaterial->SetTextureName(m_strGlobalPath + texturePath);
	}

	//Set the Material
	theGeometry->SetCurrentMaterial(tmpMaterial);
}

Node* ImporterWithAssimp::GetRootNode()
{
	return m_pkRootNode;
}